#include "lcd.h"
#include "lcd_bmp.h"
/******************************************************************** 
  *  函数名称：  show_bmp_image 
  *  功能描述：  在 LCD 上显示指定的 BMP 图片 
  *  输入参数：  文件路径 
  *  返  回  值：  成功返回 0,  失败返回-1 
  ********************************************************************/
static int show_bmp_image(const char *path)
{
    bmp_file_header file_h;
    bmp_info_header info_h;
    unsigned char *line_buf=NULL;  // 修改缓冲区类型为unsigned char*
    unsigned long line_bytes;//BMP 图像一行的字节的大小
    unsigned int min_h,min_bytes;
    int color_c_index=0;
    int fd=-1;
    int j=0;
    if(0>(fd=open(path,O_RDONLY)))
    {
        perror("open");
        return -1;
    }
     /*  读取 BMP 文件头  */ 
    if (sizeof(bmp_file_header) != 
    read(fd, &file_h, sizeof(bmp_file_header))) { 
        perror("read error"); 
        close(fd); 
        return -1; 
    } 
    if (0 != memcmp(file_h.type, "BM", 2)) { 
        fprintf(stderr, "it's not a BMP file\n"); 
        close(fd); 
        return -1; 
    }
    if(sizeof(bmp_info_header)!=
    read(fd,&info_h,sizeof(bmp_info_header)))
    {
        perror("read error"); 
        close(fd); 
        return -1;
    }
#ifdef lcd_info_show
    printf("文件名称: %s\n"
        "文件大小: %d\n" 
        "位图数据的偏移量: %d\n" 
        "位图信息头大小: %d\n" 
        "图像分辨率: %d*%d\n" 
        "像素深度: %d\n",path, file_h.size, file_h.offset, 
        info_h.size, info_h.width, info_h.height, 
        info_h.bpp);
#endif // DEBUG
    if(-1==lseek(fd,file_h.offset,SEEK_SET))
    {
        perror("lseek");
        close(fd);
        return -1;
    }
    //line_bytes=info_h.width*info_h.bpp/8;
    line_bytes = (info_h.width * info_h.bpp / 8 + 3) & ~3;
    line_buf=malloc(line_bytes);
    if(NULL==line_buf)
    {
        fprintf(stderr,"malloc error\n");
        close(fd);
        return -1;
    }
    if(line_length>line_bytes)
    {
        min_bytes=line_bytes;
    }else{
        min_bytes=line_length;
    }
    if(info_h.bpp==24)
    {
        color_c_index=1;//添加对rgb888的支持
        // 添加颜色格式转换支持
        line_bytes = (info_h.width * 3 + 3) & ~3; // 确保4字节对齐
    }
    if (0 < info_h.height) {//倒向位图 
        if (info_h.height > height) 
        { 
            min_h = height; 
            lseek(fd, (info_h.height - height) * line_bytes, SEEK_CUR); 
            screen_base += width * (height - 1);        //定位到屏幕左下角位置 
        } else { 
                min_h = info_h.height; 
                screen_base += width * (info_h.height - 1); 
            } 

        for (j = min_h; j > 0; screen_base -= width, j--) 
        { 
            read(fd, line_buf, line_bytes);
            // 添加颜色转换处理
            if (color_c_index) {
                for (int i = 0; i < info_h.width; i++) {
                    unsigned char *p = line_buf + i * 3;
                    // 添加边界检查
                    if (i >= width) break;
                    // 修正颜色分量顺序并增加gamma校正
                    unsigned int b = p[0] * 0.9;
                    unsigned int g = p[1] * 1.1;
                    unsigned int r = p[2] * 0.95;
                    unsigned int rgb888 = (r << 16) | (g << 8) | b;
                    screen_base[i] = rgb888_to_rgb565(rgb888);
                }
            }
            else {
                memcpy(screen_base, line_buf, min_bytes);
            }
        }

    } else {    //正向位图 
        int temp = 0 - info_h.height;      //负数转成正数 
        if (temp > height) 
            min_h = height; 
        else 
            min_h = temp;
            
        for (j = 0; j < min_h; j++, screen_base += width) 
        { 
            read(fd, line_buf, line_bytes); 
            memcpy(screen_base, line_buf, min_bytes); 
        } 
    } 
/*  关闭文件、函数返回  */ 
close(fd); 
free(line_buf); 
return 0; 
}
void BmpToLcd(int argc,char *argv[])
{
    int fd; 
        /*  传参校验  */ 
        if (2 != argc) { 
                fprintf(stderr, "usage: %s <bmp_file>\n", argv[0]); 
                exit(-1); 
        }
        if (0 > (fd = open("/dev/fb0", O_RDWR))) { 
            perror("open error"); 
            exit(EXIT_FAILURE); 
        }
    LcdGetInfo(fd);
    /*  将显示缓冲区映射到进程地址空间  */ 
    screen_base = mmap(NULL, screen_size, PROT_WRITE, MAP_SHARED, fd, 0); 
        if (MAP_FAILED == (void *)screen_base) { 
                perror("mmap error"); 
                close(fd); 
                exit(EXIT_FAILURE); 
        } 
    memset(screen_base, 0xFF, screen_size);
    show_bmp_image(argv[1]); 
    /*  退出  */ 
    munmap(screen_base, screen_size);    //取消映射 
    close(fd);    //关闭文件 
    exit(EXIT_SUCCESS);        //退出进程
}